import { Form, message, Modal } from "ant-design-vue";
import { reactive, Ref, ref } from "vue";
import { useI18n } from "/@/locales/useI18n";
import { coverObj } from "/@/utils/common";
import { PaginationParam, PaginationResult, DeleteParams } from "/@/api/models";
import { usePagination } from "/@/hooks/web/usePagination";
import CURD, { CURDApiOptions } from "/@/api/curd";
import { ResponseData } from "/@/api/req";
import { cloneDeep } from "lodash-es";
import dayjs from "dayjs";
const useForm = Form.useForm;
interface Record {
  id?: number | string;
}
export interface ShowModalOptions {
  title?: string;
}
interface Rule {
  required?: boolean;
  message?: string;
}
interface FieldRule {
  [key: string]: Rule[];
}
export interface CURDOptions<T, K> extends CURDApiOptions {
  createTitle?: string;
  updateTitle?: string;
  deleteTitle?: string;
  deleteMessage?: string;
  modelRef: any;
  fieldRules?: (FieldRule | string)[];
  //是否分页
  isPagination: boolean;
  create?: (params: T) => Promise<T | null | ResponseData<T>>;
  update?: (params: T) => Promise<T | null | ResponseData<T>>;
  delete?: (params: DeleteParams) => Promise<boolean | null | ResponseData<boolean>>;
  //获取分页数据
  getPage?: (params: PaginationParam<K>) => Promise<PaginationResult<T> | null | ResponseData<PaginationResult<T>>>;
  //获取所有数据
  getData?: (params?: K) => Promise<T[] | null | ResponseData<T[]>>;
  //当不定义getPage 和 getData时，该参数生效，调用外界的加载数据方法
  reloadTable?: (params?: K | PaginationParam<K>) => void;
  onBeforeShowModal?: (data?: T) => void;
  onBeforeOk?: (data: T, isCreate: boolean) => void | boolean;
  onLoadSuccess?: (data: T[]) => void;
  dateFields?: string[];
}
export enum CURDAction {
  CREATE = "CREATE",
  UPDATE = "UPDATE",
}
/**
 * 适用于1个表格 1个窗口的增删查改页面
 */
export function useCURD<T extends Record, K>(options: CURDOptions<T, K>) {
  const { t } = useI18n();
  options = Object.assign(
    {
      createTitle: t("common.add"),
      updateTitle: t("common.edit"),
      deleteTitle: t("common.tip"),
      deleteMessage: t("common.confirmDel"),
    },
    options
  );
  let curd: any = null;
  if (options.getPageUrl || options.getDataUrl || options.cudUrl) {
    const apiOptions: CURDApiOptions = {
      getPageUrl: options.getPageUrl,
      cudUrl: options.cudUrl,
      getDataUrl: options.getDataUrl,
    };
    if (options.getDataMethod) {
      apiOptions.getDataMethod = options.getDataMethod;
    }
    if (options.getPageMethod) {
      apiOptions.getPageMethod = options.getPageMethod;
    }
    curd = new CURD<T, K>(apiOptions);
    if (!options.create) {
      options.create = curd.create.bind(curd);
    }
    if (!options.update) {
      options.update = curd.update.bind(curd);
    }
    if (!options.delete) {
      options.delete = curd.delete.bind(curd);
    }
    if (!options.getPage && options.getPageUrl) {
      options.getPage = curd.getPage.bind(curd);
    }
    if (!options.getData && options.getDataUrl) {
      options.getData = curd.getData.bind(curd);
    }
  }
  let pagination,
    tableData: Ref<any[]> = ref([]),
    tableLoading: Ref<boolean> = ref(false),
    reloadTable: (params?: K | PaginationParam<K>) => void = () => {};
  let cacheParam: any = {};
  if (options.isPagination && options.getPage) {
    const { refreshPagination, pagination: innerPagination, pageData, loading } = usePagination<T, K>(options.getPage);
    tableData = pageData;
    tableLoading = loading;
    reloadTable = refreshPagination;
    pagination = innerPagination;
  } else if (options.getData) {
    reloadTable = async (params?: K | PaginationParam<K>) => {
      if (params) {
        Object.assign(cacheParam, params);
      } else if (params === null) {
        cacheParam = {};
      }
      tableLoading.value = true;
      const data = await (options.getData as (params?: K) => Promise<T[] | null>)(cacheParam);
      if (data && options.onLoadSuccess) {
        options.onLoadSuccess(data);
      }
      tableLoading.value = false;
      tableData.value = data || [];
    };
  } else if (options.reloadTable) {
    reloadTable = options.reloadTable;
  }
  const curAction = ref<CURDAction>(CURDAction.CREATE);
  let curId: number | undefined | string = 0;
  const modalVisible = ref(false);
  const modalTitle = ref("");
  let rulesRef = {};
  if (!options.fieldRules) {
    options.fieldRules = [];
    for (const key in options.modelRef) {
      options.fieldRules.push(key);
    }
  }
  options.fieldRules.forEach((item) => {
    if (typeof item === "string") {
      rulesRef[item] = [
        {
          required: true,
          message: "该字段必须填写",
        },
      ];
    } else {
      Object.assign(rulesRef, item);
    }
  });
  rulesRef = reactive(rulesRef);
  const { validate, validateInfos, resetFields } = useForm(options.modelRef, rulesRef);
  const showCreateModal = (showOptions?: ShowModalOptions) => {
    showOptions = showOptions || {};
    curAction.value = CURDAction.CREATE;
    if (showOptions.title) {
      modalTitle.value = showOptions.title;
    } else {
      modalTitle.value = options.createTitle as string;
    }
    resetFields();
    options.onBeforeShowModal && options.onBeforeShowModal();
    modalVisible.value = true;
  };
  const showUpdateModal = (record: T, showOptions?: ShowModalOptions) => {
    showOptions = showOptions || {};
    curAction.value = CURDAction.UPDATE;
    curId = record.id;
    const model = cloneDeep(record);
    if (options.dateFields) {
      options.dateFields.forEach((key) => {
        if (model[key] && typeof model[key] === "string") {
          model[key] = dayjs(model[key]);
        }
      });
    }
    options.onBeforeShowModal && options.onBeforeShowModal(model);
    coverObj(options.modelRef, model);
    if (showOptions.title) {
      modalTitle.value = showOptions.title;
    } else {
      modalTitle.value = options.createTitle as string;
    }
    modalVisible.value = true;
  };
  const modalOk = () => {
    validate().then(async () => {
      const model = cloneDeep(options.modelRef);
      let action = options.create;
      if (curAction.value === CURDAction.UPDATE && curId) {
        model.id = curId;
        action = options.update;
      }
      if (!action) return;
      if (options.dateFields) {
        options.dateFields.forEach((key) => {
          if (model[key] instanceof dayjs) {
            model[key] = (model[key] as dayjs.Dayjs).format("YYYY-MM-DD HH:mm:ss");
          }
        });
      }
      if (options.onBeforeOk) {
        const re = options.onBeforeOk(model, curAction.value === CURDAction.CREATE);
        if (re === false) {
          return;
        }
      }
      const result = await action(model);
      if (result) {
        message.success(t("common.operSuccess"));
        modalVisible.value = false;
        await reloadTable();
      }
    });
  };
  const deleteRecord = (record: T) => {
    Modal.confirm({
      content: options.deleteMessage,
      title: options.deleteTitle,
      onOk: async (close: any) => {
        if (!options.delete) return;
        const result = await options.delete({
          id: record.id as string,
        });
        close();
        if (result) {
          message.success(t("common.operSuccess"));
          await reloadTable();
        }
      },
    });
  };
  return {
    modalVisible,
    modalTitle,
    validate,
    validateInfos,
    showCreateModal,
    showUpdateModal,
    modalOk,
    deleteRecord,
    pagination,
    tableData,
    tableLoading,
    reloadTable,
    curAction,
    curd,
  };
}
